PHP DAL
- een connectie met de database te maken
- één rij op basis van een id uit te lezen
- alle rijen uit te lezen
- een rij te inserten
- een rij te updaten
- een rij te deleten
Configuratiebestand
We gebruiken een ini bestand om de logingegevens van de database bij te houden. De structuur ziet er zo uit:
[local] database = mmt username = root password = host = 127.0.0.1 port = 3306 driver = mysql [global] database = Docent1 username = Docent1 password = Docent_XXXXXXX host = xxx.xxx.xxx.xxx port = 3306 driver = mysql
Het Helpers.php bestand
In dit bestand staat de Helpers
klasse met enkele handige methoden die we overal kunnen gebruiken. Het volledige pad van dat bestand is vendor/anormapart/Helpers.php. Nog even ter herinnering, bestandsnamen waarin een klasse staat worden in pascalnotatie geschreven.
De eerste functie converteert de HTML-tekens, wat helpt bij het voorkomen van XSS-aanvallen.
De tweede helper functie schoont een array met de ingegeven waarden in een formulier op ($_POST
of een kopie ervan).
<?php /** * Created by ModernWays * User: Jef Inghelbrecht * Date: 10/04/2019 * Revision: 8/04/2020 * Time: 10:32 */ namespace AnOrmApart; class Helpers { public static function escape($html) { return htmlspecialchars($html, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); } public static function escapeAssoc($assoc) { // cleanup $_POST array // omdat array_map niet met een associatieve array werkt // geven we die in twee keer door, de eerste keer de key's // de tweede keer de values return array_map( function($key, $value) { return array($key => self::escape($value)); }, array_keys($assoc), array_values($assoc) ); } public static function cleanUpInput($data) { $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data); return $data; } public static function isAssoc($var) { return is_array($var) && array_diff_key($var, array_keys(array_keys($var))); } /** ------------------ escapeArray -------------------------- * * Escapes scripts tags from input * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $array with the values to be escaped * @return array */ public static function escapeArray($array) { $cleanArray = array(); foreach ($array as $key => $value) { $cleanArray[$key] = self::escape($value); } return $cleanArray; } }
Het Dal.php bestand
- Inleiding
De DAL of Data Access Layer is de laag waarmee je de database kan benaderen. Een overzicht van de n-tier architectuur vind je op n-tier architectuur.
-
De
Dal
klasse staat in een namespace met de naam van de vendor, namelijkAnOrmApart
:namespace AnOrmApart; class Dal { ... }
- De klasse bevat de volgende velden:
/** * @var $connection The main connection to the database * @var $configLocation path to config file, default is data/config.ini * @var $message feedback for the user of this class */ protected static $connection; public static $configLocation = "data/config.ini"; protected static $message;
- De klasse bevat de volgende getter:
public static function getMessage() { return self::$message; }
- De klasse bevat twee dal-helpersfuncties:
- De volgende functie bepaalt het gegevenstype van de te updaten kolom in de tabel:
private static function getParameterType($value) { switch (gettype($value)) { case 'boolean' : $paramType = \PDO::PARAM_BOOL; break; case 'integer' : $paramType = \PDO::PARAM_INT; break; case 'NULL' : $paramType = \PDO::PARAM_STR; $value = ''; break; default : $paramType = \PDO::PARAM_STR; break; } return $paramType; }
- De volgende helperfunctie maakt een string van de sleutelwaarden in een array die door een komma gescheiden worden. Zo'n string hebben we nodig om de kolommen aan te geven in een SQL query:
/** ------------------ columnsCommaSeparated -------------------------- * * We cannot use explode to concatenate the items of an array in een comma * separated string because the items must be enclosed between backticks * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $columnSelectArray array with items to be concatenated * @return string */ private static function columnsCommaSeparated($columnSelectArray) { $columns = ''; if (isset($columnSelectArray)) { foreach($columnSelectArray as $item) { $columns .= "`{$item}`, "; } } else { $columns = '*'; } $columns = rtrim($columns, ", "); return $columns; }
- De volgende functie bepaalt het gegevenstype van de te updaten kolom in de tabel:
- De methode om een connectie met de database te maken laadt het config.ini bestand waarin de aanmeldingsgegevensstaan. De feedback wordt bijgehouden in het
$message
veld. Als de verbinding gemaakt is retourneert die methodetrue
:/** ------------------ connect -------------------------- * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $connectionName de naam van de sectie van het ini bestand * @return boolean if connected true, else false */ public static function connect($connectionName='local') { $success = false; $options = array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION && \PDO::ERRMODE_WARNING ); if (self::$connection !== null) { self::$message = 'Connectie is al gemaakt.'; $success = true; } else { // true want we willen secties inlezen // met dank aan Sam Wouters voor het idee om // een ini bestand te gebruiken!!!!! $config = parse_ini_file(self::$configLocation, true); try { $database = $config[$connectionName]['database']; $userName = $config[$connectionName]['username']; $password = $config[$connectionName]['password']; $driver = $config[$connectionName]['driver']; $host = $config[$connectionName]['host']; $port = $config[$connectionName]['port']; $dsn = "{$driver}:host={$host}:{$port};dbname={$database}"; self::$connection = new \PDO($dsn, $userName, $password, $options); self::$message = "Connectie met $database is gemaakt."; $success = true; } catch (\PDOException $e) { self::$message = $e->getMessage(); } } return $success; }
- Met de
create
methode kunnen we één of meerdere rijen in de tabel inserten. De functie retourneert het aantal rijen die werden geïnserted:/** ------------------ create -------------------------- * creates one or multiple rows in a table * * @lastmodified 27/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $postArray the values used to insert one row in table is $postArray is an associative array, * if it is an array with more than one associative array in it, multiple rows are inserted. * @param $columnForMessage the columnname to be used ihe feedback for the user * @return int the number of inserted rows in the table */ public static function create($tableName, $postArray, $columnForMessage='Name') { $success = 0; if (self::connect()) { // if $postArray is one row, thus an associative array if (\ModernWays\Helpers::isAssoc($postArray)) { $rows = [$postArray]; } else { $rows = $postArray; } foreach ($rows as $item) { $row = self::cleanUp($item); //echo '<pre>'; //var_dump($row); //echo '</pre>'; try { // we kunnen hier niet implode gebruiken omdat de kolommen // 'ontsnapt' moeten worden en tussen backticks geplaatst moeten worden. $columns = self::columnsCommaSeparated(array_keys($row)); $sql = sprintf("INSERT INTO %s (%s) VALUES (:%s)", $tableName, $columns, implode(', :', array_keys($row))); echo $sql; $statement = self::$connection->prepare($sql); $success = $statement->execute($row); if ($success == 0) { self::$message = "{$row[$columnForMessage]} in tabel {$tableName} is niet toegevoegd."; } else { self::$message = "{$row[$columnForMessage]} in tabel {$tableName} is toegevoegd."; } } catch (\PDOException $exception) { self::$message = "Rij is niet toegevoegd in {$tableName}!<br .>"; self::$message .= $exception->getMessage(); } } } return $success; }
- De
delete
methode verwijdert de rij met de opgegeven id uit de tabel:/** ------------------ delete -------------------------- * * Deletes one row in a table * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $value the value to be looked up in the WHERE clause * @param $columnName the columnname to be looked up in the WHERE clause; default is the Id column * @return boolean if row is deleted return true otherwise false */ public static function delete($tableName, $value, $columnName = 'Id') { $success = 0; if (self::connect()) { try { $sql = "DELETE FROM $tableName WHERE $columnName = :$columnName"; $statement = self::$connection->prepare($sql); $statement->bindParam(":$columnName", $value); $statement->execute(); $success = $statement->rowCount(); if ($success == 0) { self::$message = "De rij met $columnName $value is niet verwijderd uit $tableName!"; } else { self::$message = "De rij met $columnName $value is verwijderd uit $tableName!"; } } catch (\PDOException $exception) { self::$message = "Fout: de rij met $columnName = $value is niet verwijderd uit $tableName!<br />{$exception->getMessage()}"; } } return $success; }
- Met de
readAll
methode haal je alle rijen op uit een tabel. Je kan opgeven welke kolommen je wilt selecteren en in welke volgorde de rijen geordend worden. De methode retourneert een associatieve array met de rijen:/** ------------------ readAll -------------------------- * * Reads all rows from a table * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $value the value to be looked up in the WHERE clause * @param $orderBy the columnname to be sorted on; default is the Name column * @param $columnSelectArray array with columnnames to be selected from the table. Default is all (*). */ public static function readAll($tableName, $orderBy = 'Name', $columnSelectArray = null) { $result = null; if (self::connect()) { try { $columns = self::columnsCommaSeparated($columnSelectArray); $sql = "SELECT {$columns} FROM {$tableName} ORDER BY $orderBy"; $statement = self::$connection->prepare($sql); $statement->execute(); $result = $statement->fetchAll(); // An empty array is returned if there are zero results to fetch, or FALSE on failure if ($result === false) { self::$message = "Er is iets foutgelopen bij het inlezen van $tableName."; } else { if (isset($result)) { $rowCount = $statement->rowCount(); self::$message = "{$rowCount} rij(en) van $tableName ingelezen."; } else { self::$message = "$tableName is leeg."; } } } catch (\PDOException $exception) { self::$message = $exception->getMessage(); } } return $result; }
- De methode
readAllWhere
retourneert alle rijen die overeenkomen met een opgegegeven voorwaarde:/** ------------------ readAllWhere -------------------------- * * Reads all rows from a table according to a where clause * PDO::FETCH_ASSOC removes all the numeric keys and only leaves with associated keys. * * @lastmodified 27/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $value the value to be looked up in the WHERE clause * @param $columnName the columnname to be looked up in the WHERE clause; default is the Id column * @param $orderBy the columnname to be sorted on; default is the Name column * @param $columnSelectArray array with columnnames to be selected from the table. Default is all (*). * @return array selected rows from the table, colum as key-value pairs */ public static function readAllWhere($tableName, $value, $columnName = 'Id', $orderBy = 'Name', $columnSelectArray = null) { $result = null; if (self::connect()) { try { $columns = self::columnsCommaSeparated($columnSelectArray); $sql = "SELECT {$columns} FROM {$tableName} WHERE {$columnName} = {$value} ORDER BY $orderBy"; $statement = self::$connection->prepare($sql); $statement->execute(); $result = $statement->fetchAll(\PDO::FETCH_ASSOC); self::$message = "Alle rijen van $tableName zijn ingelezen."; } catch (\PDOException $exception) { self::$message = $exception->getMessage(); self::$message = "De tabel $tableName is leeg."; } } return $result; }
- De
readAllLikeX
methode haalt alle rijen op die de zoekwaarde ergens in de opgegeven kolom heeft staan:/** ------------------ readAllLikeX -------------------------- * * Reads all rows from a table according to a where clause. * Selects all rows with a ColumnName that have the given value in any position. * PDO::FETCH_ASSOC removes all the numeric keys and only leaves with associated keys. * * @lastmodified 27/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $value the value to be looked up in the WHERE clause * @param $columnName the columnname to be looked up in the WHERE clause; default is the Id column * @param $columnSelectArray array with columnnames to be selected from the table. Default is all (*). * @return array selected rows from the table, colum as key-value pairs */ public static function readAllLikeX($tableName, $value, $columnName = 'Name', $orderBy = 'Name', $columnSelectArray = null) { $result = null; if (self::connect()) { try { $columns = self::columnsCommaSeparated($columnSelectArray); $sql = "SELECT {$columns} FROM {$tableName} WHERE $columnName LIKE CONCAT('%', :$columnName, '%') ORDER BY $orderBy"; $statement = self::$connection->prepare($sql); $statement->bindParam(":{$columnName}", $value, \PDO::PARAM_STR); $statement->execute(); $result = $statement->fetchAll(\PDO::FETCH_ASSOC); self::$message = "Alle rijen van $tableName zijn ingelezen."; } catch (\PDOException $exception) { self::$message = $exception->getMessage(); self::$message = "De tabel $tableName is leeg."; } } return $result; }
- De
readOne
methode haalt 1 rij op uit de tabel:/** ------------------ readOne -------------------------- * * Reads one row from a table * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $value the value to be looked up in the WHERE clause * @param $columnName the columnname to be looked up in the WHERE clause; default is the Id column * @param $columnSelectArray array with columnnames to be selected from the table. Default is all (*). * @return array dictionary, keys are columnames, values are the corresponding values */ public static function readOne($tableName, $value, $byColumnName = 'Id', $columnSelectArray = null) { if (self::connect()) { $result = null; try { $columns = self::columnsCommaSeparated($columnSelectArray); // echo $columns; $sql = "SELECT {$columns} FROM {$tableName} WHERE {$byColumnName} = :{$byColumnName}"; $statement = self::$connection->prepare($sql); $statement->bindParam(":{$byColumnName}", $value, \PDO::PARAM_STR); $statement->execute(); $result = $statement->fetch(\PDO::FETCH_ASSOC); if ($result) { self::$message = "De rij met de $byColumnName = $value is ingelezen uit de tabel $tableName."; } else { self::$message = "De rij met de $byColumnName = $value is niet ingelezen uit de tabel $tableName."; } } catch (\PDOException $exception) { self::$message = "De rij met de $byColumnName = $value is niet ingelezen uit de tabel $tableName.<br {$exception->getMessage()}/>"; } } return $result; }
- Met de update methode kan je een rij in de tabel wijzigen:
/** ------------------ update -------------------------- * * Update one row in a table * * @lastmodified 26/01/2019 * @author Jef Inghelbrecht - Entreprise de Modes et de Manieres Modernes - e3M * @version 1.0 * @param $tableName table name * @param $postArray the values used to update row in table * @param $columnForMessage the columnname to be used ihe feedback for the user * @return int the number of updated rows in the table */ public static function update($tableName, $postArray, $columnForMessage = 'Name') { $success = 0; if (self::connect()) { $row = self::cleanUp($postArray); try { // 'ontsnapt' moeten worden en tussen backticks geplaatst moeten worden. $sql= "UPDATE $tableName SET "; foreach($row as $key => $value) { if ($key !== 'Id') { $sql .= "`{$key}` = :{$key}, "; } } $sql = rtrim($sql, ", "); $sql .= ' WHERE Id = :Id'; //echo '<pre>'; //echo $sql; //var_dump($row); //echo '<pre>'; $statement= self::$connection->prepare($sql); $statement->execute($row); $success = $statement->rowCount(); if ($success == 0) { self::$message = "{$row[$columnForMessage]} in tabel {$tableName} is niet gevonden."; } else { self::$message = "{$row[$columnForMessage]} in tabel {$tableName} is geüpdated."; } } catch (\PDOException $exception) { self::$message = "{$row[$columnForMessage]} is in tabel {$tableName} niet geüpdated.<br /> Syntax error: {$exception->getMessage()}"; } } return $success; }